package actor

import (
	"fmt"
	"go_learn/mlog"
	"math"
	"reflect"
	"runtime"
	"strings"
	"sync"
)

func getFunctionName(i interface{}, seps ...rune) string {
	// 获取函数名称
	fn := runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name()
	// 用 seps 进行分割
	fields := strings.Split(fn, ".")
	if size := len(fields); size > 0 {
		return fields[size-1]
	}
	return ""
}

type BaseActor struct {
	appInst      App
	service      Service
	addr         uint
	sessionIndex uint
	highChan     chan interface{}
	lowChan      chan interface{}
	closeChan    chan bool
	sessionMap   map[uint]*Session
	msgHandle    map[EMsgType]map[string]*FunctionInfo
	scheduler    func(closeSig chan bool)
}

func (slf *BaseActor) OnInit(app App, s Service, addr uint) {
	slf.addr = addr
	slf.appInst = app
	slf.service = s
	slf.sessionIndex = 0
	slf.msgHandle = make(map[EMsgType]map[string]*FunctionInfo)
	slf.closeChan = make(chan bool, 0)
	slf.lowChan = make(chan interface{}, 100)
	slf.highChan = make(chan interface{}, 10)
	slf.sessionMap = make(map[uint]*Session)
	slf.service.OnInit()
}

func (slf *BaseActor) Run(closeSig chan bool) (err error) {
	defer func() {
		if r := recover(); r != nil {
			switch r.(type) {
			case error:
				err = r.(error)
			default:
				err = fmt.Errorf("%v", r)
			}
		}
	}()
	wg := sync.WaitGroup{}
	wg.Add(1)
	go func() {
		defer func() {
			if r := recover(); r != nil {
				switch r.(type) {
				case error:
					err = r.(error)
				default:
					err = fmt.Errorf("%v", r)
				}
			}
			wg.Done()
		}()
		slf.service.Run(closeSig)
	}()
	//或者应该考虑用fork去执行Run?
	go slf.scheduler(closeSig) //在此处执行的话,可能会在Run执行完之前就有消息到来并执行.但是如果不在此处执行则无法在Run内执行Call等操作
	wg.Wait()
	return
}

func (slf *BaseActor) GetAddr() uint {
	return slf.addr
}

func (slf *BaseActor) GetService() Service {
	return slf.service
}

func (slf *BaseActor) OnDestroy() {
	slf.service.OnDestroy()
}

func (slf *BaseActor) GetRpcChan() chan interface{} {
	return slf.lowChan
}

func (slf *BaseActor) Close() {
	slf.closeChan <- true
}

func (slf *BaseActor) RegistHandle(msgType EMsgType, name string, handle interface{}) {
	// fn := runtime.FuncForPC(reflect.ValueOf(handle).Pointer()).Name()
	// name := getFunctionName(fn)
	if name == "" {
		panic(fmt.Errorf("can't find function[%v] name!", handle))
	}
	m, ok := slf.msgHandle[msgType]
	if !ok {
		m = make(map[string]*FunctionInfo)
		slf.msgHandle[msgType] = m
	}
	if _, ok := m[name]; ok {
		panic(fmt.Errorf("function %v: already registered", name))
	}
	finfo := &FunctionInfo{
		Function:  reflect.ValueOf(handle),
		FuncType:  reflect.ValueOf(handle).Type(),
		Goroutine: false,
	}
	finfo.InType = []reflect.Type{}
	for i := 0; i < finfo.FuncType.NumIn(); i++ {
		rv := finfo.FuncType.In(i)
		finfo.InType = append(finfo.InType, rv)
	}
	numOut := finfo.FuncType.NumOut()
	if numOut == 2 {
		out1 := finfo.FuncType.Out(1)
		outV := reflect.New(out1)
		if _, ok := outV.Interface().(error); ok {
			panic(fmt.Errorf("function %v: second out type[%v] is wrong", name, out1))
		}
	} else if numOut != 2 && numOut != 0 {
		panic(fmt.Errorf("function %v: out num[%d] is wrong", name, numOut))
	}
	m[name] = finfo
}

func (slf *BaseActor) Send(addr interface{}, msgType EMsgType, method string, param ...interface{}) (err error) {
	msg := RpcMsg{
		Source:  slf.GetAddr(),
		Session: slf.incrSession(),
		MsgType: msgType,
		Method:  method,
		Params:  param,
	}
	return slf.appInst.Send(addr, &msg)
}

func (slf *BaseActor) AsynCall(addr interface{}, msgType EMsgType, method string, cb func(interface{}, error), param ...interface{}) (err error) {
	s := slf.newSession(nil, cb)
	msg := RpcMsg{
		Source:  slf.GetAddr(),
		Session: s.id,
		MsgType: msgType,
		Method:  method,
		Params:  param,
		retChan: slf.highChan,
	}
	return slf.appInst.Send(addr, &msg)
}

func (slf *BaseActor) SyncCall(addr interface{}, msgType EMsgType, method string, param ...interface{}) (rst interface{}, err error) {
	rc := make(chan interface{}, 0)
	msg := RpcMsg{
		Source:  slf.GetAddr(),
		Session: slf.incrSession(),
		MsgType: msgType,
		Method:  method,
		Params:  param,
		retChan: rc,
	}
	err = slf.appInst.Send(addr, &msg)
	if err != nil {
		return
	}
	retMsg := <-rc
	rst = retMsg.(*RetInfo).ret
	err = retMsg.(*RetInfo).err
	return rst, err
}

func (slf *BaseActor) incrSession() uint {
	if slf.sessionIndex == math.MaxUint32 {
		slf.sessionIndex = 0
	}
	slf.sessionIndex++
	return slf.sessionIndex
}

func (slf *BaseActor) newSession(rc chan *RetInfo, cb func(interface{}, error)) *Session {
	index := slf.incrSession()
	session := &Session{index, rc, cb}
	slf.sessionMap[index] = session
	return session
}

func (slf *BaseActor) rmSession(id uint) *Session {
	session := slf.sessionMap[id]
	delete(slf.sessionMap, id)
	return session
}

func (slf *BaseActor) _runHandle(handle *FunctionInfo, msg *RpcMsg) (ret interface{}, err error) {
	defer func() {
		if r := recover(); r != nil {
			switch r.(type) {
			case error:
				err = r.(error)
			default:
				err = fmt.Errorf("%v", r)
			}
			mlog.Errorf("actor[%s] call method[%s] occur error[%s]", slf.service.Name(), handle.FuncType.Name(), err.Error())
		}
		//此处只处理在本函数内报错情况下的消息返回,正常情况在_runHandle中返回
		if msg.retChan == nil {
			return
		}
		retMsg := RetInfo{
			session: msg.Session,
			ret:     ret,
			err:     err,
		}
		msg.retChan <- &retMsg
	}()
	//
	f := handle.Function
	fType := handle.FuncType
	fInType := handle.InType
	params := msg.Params
	if len(params)+1 != fType.NumIn() {
		return nil, fmt.Errorf("actor[%s] call method[%v] The number of params %v is not adapted.%v", slf.service.Name(), fType, params, f.String())
	}
	var in []reflect.Value
	in = make([]reflect.Value, len(params)+1)
	in[0] = reflect.ValueOf(msg.Source)
	for k, v := range params {
		if fInType[k+1] != reflect.TypeOf(v) {
			return nil, fmt.Errorf("actor[%s] call method[%v] params index[%d] can't match[%v--%v]", slf.service.Name(), fType, k, fInType[k+1], v)
		}
		in[k+1] = reflect.ValueOf(v)
	}
	out := f.Call(in)
	if len(out) != 0 { //prepare out paras
		ret = out[0].Interface()
		switch out[1].Interface().(type) {
		case error:
			err = out[1].Interface().(error)
		}
		return
	}
	return
}

func (slf *BaseActor) dispatch(msg *RpcMsg) {
	var err error
	defer func() {
		if r := recover(); r != nil {
			switch r.(type) {
			case error:
				err = r.(error)
			default:
				err = fmt.Errorf("%v", r)
			}
			mlog.Errorf("actor[%d] recv[%d] method[%s] occur error[%s]", slf.GetAddr(), msg.Source, msg.Method, err.Error())
		}
		//此处只处理在本函数内报错情况下的消息返回,正常情况在_runHandle中返回
		if msg.retChan == nil || err == nil {
			return
		}
		retMsg := RetInfo{
			session: msg.Session,
			err:     err,
		}
		msg.retChan <- &retMsg
	}()
	if _, ok := slf.msgHandle[msg.MsgType]; !ok {
		err = fmt.Errorf("actor[%s] don't deal msgType[%v]", slf.service.Name(), msg.MsgType)
		return
	}
	handle, ok := slf.msgHandle[msg.MsgType][msg.Method]
	if !ok {
		err = fmt.Errorf("actor[%s] don't deal msgType[%v] method[%s]", slf.service.Name(), msg.MsgType, msg.Method)
		return
	}
	mlog.Debugf("actor[%d] recv[%d] method[%s] start", slf.GetAddr(), msg.Source, msg.Method)
	if handle.Goroutine {
		go slf._runHandle(handle, msg)
	} else {
		slf._runHandle(handle, msg)
	}
}
